#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include "engine.h"
#include "commands_python.h"

void remove_directory_recursive(const char* name) {

	// Open folder
	DIR *d = opendir(name);

	// Folder doesn't exist - return
	if (!d) {
		return;
	}

	// For every file in engine
	struct dirent *de;
	while (de = readdir(d)) {

		// Ignore . and .. directories
		if (strcmp(de->d_name, ".") == 0
		||  strcmp(de->d_name, "..") == 0) {
			continue;	
		}

		// Get full path of file
		char src_path[1024];
		strcpy(src_path, name);
		strcat(src_path, de->d_name);

		// Do directories recursively
		struct stat path_stat;
		stat(src_path, &path_stat);
		if (S_ISDIR(path_stat.st_mode)) {
			strcat(src_path, "/");
			remove_directory_recursive(src_path);
			continue;
		}

		// Remove file
		remove(src_path);

	}

	closedir(d);

}

void copy_file(const char* dst_name, const char* src_name) {
	
	FILE *dst = fopen(dst_name, "w");
	if (!dst) {
		printf("error opening file: %s\n", dst_name);
		return;
	}

	FILE *src = fopen(src_name, "r");
	if (!src) {
		printf("error opening file: %s\n", src_name);
		return;
	}

	char *buffer = malloc(sizeof(char) *1024);

	int written_chars;
	while (!feof(src)) {
		for (int i = 0; i < 1024; i++) { buffer[i] = '\0'; }
		written_chars = fscanf(src, "%1023c", buffer);
		buffer[1023] = '\0';
		fprintf(dst, buffer);
	}

	fclose(dst);
	fclose(src);
}

void copy_directory_recursive(const char *dst_name, const char* src_name) {

	// Openg target engine
	DIR *d = opendir(src_name);

	// For every file in engine
	struct dirent *de;
	while (de = readdir(d)) {

		// Ignore . and .. directories
		if (strcmp(de->d_name, ".") == 0
		||  strcmp(de->d_name, "..") == 0) {
			continue;	
		}

		// Get full path of source (path + filename)
		char src_path[1024];
		strcpy(src_path, src_name);
		strcat(src_path, de->d_name);

		// Get target file
		char dst_path[1024];
		strcpy(dst_path, dst_name);
		strcat(dst_path, de->d_name);

		// Do directories recursively
		struct stat path_stat;
		stat(src_path, &path_stat);
		if (S_ISDIR(path_stat.st_mode)) {
			strcat(src_path, "/");
			strcat(dst_path, "/");
			mkdir(dst_path, 0777);
			copy_directory_recursive(dst_path, src_path);
			continue;
		}

		// Copy file
		copy_file(dst_path, src_path);

	}

	closedir(d);
}

void add_text_recursive(const char *src, const char *key, const char *txt) {

	// Openg target engine
	DIR *d = opendir(src);

	// For every file in engine
	struct dirent *de;
	while (de = readdir(d)) {

		// Ignore . and .. directories
		if (strcmp(de->d_name, ".") == 0
		||  strcmp(de->d_name, "..") == 0) {
			continue;	
		}

		// Get full path of source (path + filename)
		char src_path[1024];
		strcpy(src_path, src);
		strcat(src_path, de->d_name);

		// Do directories recursively
		struct stat path_stat;
		stat(src_path, &path_stat);
		if (S_ISDIR(path_stat.st_mode)) {
			strcat(src_path, "/");
			add_text_recursive(src_path, key, txt);
			continue;
		}

		// add text to key
		//copy_file(dst_path, src_path);

	}

	closedir(d);

}

void add_text(const char *file, const char *txt) {

	FILE *f = fopen(file, "a");
	if (!f) {
		printf("error %s: %s\n", f, strerror(errno));
		return;
	}

	fprintf(f, "%s", txt);

	fclose(f);
}
void replacef(const char *filename, const char *key, const char *content) {

	FILE *f = fopen(filename, "r");

	char buffer[1024];
	strcpy(buffer, filename);
	strcat(buffer, ".bck");
	FILE *bck = fopen(buffer, "w");

	while (!feof(f)) {

		for (int i = 0; i < 1024; i++) { buffer[i] = '\0'; }
		if (fscanf(f, "%1023[^@]", buffer) > 0) {
			fprintf(bck, buffer);
		}
		else {
			for (int i = 0; i < 1024; i++) { buffer[i] = '\0'; }
			fscanf(f, "@%1023[^@]@", buffer);
			if (strcmp(buffer, key) == 0) {
				FILE *ff = fopen(content, "r");
				if (!ff) {
					printf("error opening file: %s\n", content);
					return;
				}

				// Put contents of gen file
				for (int i = 0; i < 1024; i++) { buffer[i] = '\0'; }
				while (fscanf(ff, "%1023c", buffer) > 0) {
					fprintf(bck, buffer);
				}
				fclose(ff);
			}
			else {
				fprintf(bck, "@%s@", buffer);
			}
		}

	}

	fclose(f);
	fclose(bck);

	remove(filename);
	strcpy(buffer, filename);
	strcat(buffer, ".bck");
	rename(buffer, filename);

}

void replace_recursive(const char *filename, const char *key,
	const char *content_file) {

	// Open folder
	DIR *d = opendir(filename);

	// For every file in engine
	struct dirent *de;
	while (de = readdir(d)) {

		// Ignore ./ ../ and gen/
		if (strcmp(de->d_name, "."  ) == 0
		||  strcmp(de->d_name, ".." ) == 0
		||  strcmp(de->d_name, "gen") == 0) {
			continue;	
		}

		// Get full path of file
		char src_path[1024];
		strcpy(src_path, filename);
		strcat(src_path, "/");
		strcat(src_path, de->d_name);

		// Do directories recursively
		struct stat path_stat;
		stat(src_path, &path_stat);
		if (S_ISDIR(path_stat.st_mode)) {
			replace_recursive(src_path, key, content_file);
			continue;
		}

		// replace text in file
		replacef(src_path, key, content_file);

	}

	closedir(d);

}

void replace_with_gen_recursive(const char* name) {

	// Open folder
	char b[1024];
	strcpy(b, name);
	strcat(b, "/gen");
	DIR *d = opendir(b);

	// Folder doesn't exist - return
	if (!d) {
		return;
	}

	// For every file in engine
	struct dirent *de;
	while (de = readdir(d)) {

		// Ignore . and .. directories
		if (strcmp(de->d_name, ".") == 0
		||  strcmp(de->d_name, "..") == 0) {
			continue;	
		}

		// Get full path of file
		char src_path[1024];
		strcpy(src_path, name);
		strcat(src_path, "/gen/");
		strcat(src_path, de->d_name);

		// Remove file
		replace_recursive(name, de->d_name, src_path);

	}

	closedir(d);

}


/* Remove previous data of engine
 * copy template engine to target location
 */
struct avgdl_engine *initialize_engine() {

	struct avgdl_engine *engine = malloc(sizeof(struct avgdl_engine));
	engine->path = "build/c-allegro";

	// remove previous engine (if exists)
	remove_directory_recursive("build/");

	// Create build folders
	mkdir("build", 0777);
	mkdir("build/c-allegro", 0777);
	mkdir("build/c-allegro/gen", 0777);

	copy_directory_recursive("build/c-allegro/", "target/c-allegro/");

	add_text("build/c-allegro/gen/OBJECTS_INITIALIZATION", "");
	add_text("build/c-allegro/gen/OBJECTS_UPDATE", "");
	add_text("build/c-allegro/gen/OBJECTS_DRAW", "");
	printf("initialized engine\n");

	return engine;

}

/* Execute command
 * Find key and add its contents to the file with the same name
 */
void execute_command(struct avgdl_engine *engine, struct avgdl_command *cmd) {

	// Get native code from command
	struct avgdl_return *native = get_native_code(cmd);

	// Something failed
	if (!native) {
		printf("error: command '%s' is not initialized in this language\n",
			cmd->name);
		return;
	}

	// Raw data, assume initialization
	if (native->type == RAW) {

		// Objects are also declared
		if (strcmp(cmd->name, "object") == 0) {
			char object_name[1024];
			strcpy(object_name, "\tstruct object ");
			strcat(object_name, (char*) cmd->arg);
			strcat(object_name, ";\n");
			add_text("build/c-allegro/gen/OBJECTS_DECLARE", 
				object_name);
		}

		add_text("build/c-allegro/gen/OBJECTS_INITIALIZATION", 
			(char*)native->data);
	}
	else
	// Native data, add to correct file
	if (native->type == NATIVE) {
		char *key = ((char**)native->data)[0];
		char *content = ((char**)native->data)[1];

		char *prefix = "build/c-allegro/gen/";
		char *path = malloc(sizeof(char) *(strlen(prefix) +strlen(key)) +1);
		strcpy(path, prefix);
		strcat(path, key);

		add_text(path, content);
	}

	// Put native code into gen files
	printf("executed command\n");

}

char *execute_command_solo(struct avgdl_engine *engine, struct avgdl_command *cmd) {

	/*
	// Get native code from command
	char *native = get_native_code(cmd);

	// Something failed
	if (!native) {
		printf("error: command '%s' is not initialized in this language\n",
			cmd->name);
		return 0;
	}

	// Put native code into gen files
	add_text("build/c-allegro/gen/OBJECTS_INITIALIZATION", native);
	printf("executed command\n");

	*/
	return "haha";


}


/* Remove generation folder and any temporary data that does not contribute
 * to the final result
 */
void clean_engine(struct avgdl_engine *engine) {

	replace_with_gen_recursive("build/c-allegro");

	free(engine);
	printf("clean engine\n");

}

